/**
 * 上海中赢金融信息服务有限公司
 * Copyright (c) 2016-2026 hmc,Inc.All Rights Reserved.
 */
package com.sys.midware.rdb.dbRouter;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import com.sys.midware.rdb.dbRouter.DBClusterRouter.RouterSelector.Category;
import com.sys.midware.rdb.exception.RDBException;

/**
 * 数据源路由
 * 
 * @author mobing@b5m.com
 * @see com.DBClusterRouter.ocean.core.DataSourceRouter
 * @version V0.0.1-SNAPSHOT, 2015-08-13下午1:17:20
 * @description
 * 
 */
@SuppressWarnings("rawtypes")
public class DBClusterRouter {

	private static final Logger log = LoggerFactory.getLogger(DBClusterRouter.class);

	private static final Map<Object, RouterSelector> selector = new HashMap<Object, RouterSelector>();
	
	private static final ThreadLocal<RouterStrategy> strategy = new ThreadLocal<RouterStrategy>();
	
	private static final ThreadLocal<Object> resource = new ThreadLocal<Object>();
	
	@SuppressWarnings("unchecked")
	public static void bindRouterResource(Object obj, boolean force) {
		if (obj == null) {
			throw new NullPointerException("resource");
		}
		Object res = resource.get();
		if (res != null && res != obj) {
			try {
				Class clz = res.getClass();
				Method close = clz.getMethod("close");
				if (null != close) {
					close.invoke(res);
				}
			} catch (Exception e) {
				if (log.isErrorEnabled()) {
					log.error(e.getMessage(), e);
				}
			} finally {
				resource.remove();
			}
		}
		if (force) {
			resource.set(obj);
		} else {
			if (res != obj) {
				resource.set(obj);
			}
		}
	}
	
	public static void bindRouterStrategy(String key, boolean limited) {
		RouterStrategy head = strategy.get();
		if (head == null) {
			head = new RouterStrategy();
			head.setLimited(limited);
			head.setKey(key);
			strategy.set(head);
		} else {
			if (head.getTail() == null) {
				RouterStrategy tail = new RouterStrategy();
				tail.setLimited(limited);
				tail.setKey(key);
				tail.setHead(head);
				head.setTail(tail);
				strategy.set(tail);
			}
		}
	}
	
	public static String bindRouterSelector(final Object obj, Category cgy, String val, boolean ts) {
		if (obj == null || cgy == null) {
			throw new NullPointerException("Obj, Category");
		}
		if (val == null || "".equals(val.trim())) {
			throw new NullPointerException("value");
		}
		RouterSelector rs = selector.get(obj);
		if (null == rs) {
			synchronized (selector) {
				if (!selector.containsKey(obj)) {
					selector.put(obj, rs = new RouterSelector());
					rs.setCategory(cgy);
					rs.setProxyObj(obj);
					rs.setSyncDs(true);
					rs.setValue(val);
					rs.setSyncTs(ts);
				}
			}
		} else { 
			if (log.isWarnEnabled()) {
				log.warn("Selector exists");
			}
		}
		return getRouterSelector(obj);
	}
	
	@SuppressWarnings("unchecked")
	public static <T> T getRouterResource() {
		return (T) resource.get();
	}
	
	public static RouterStrategy getRouterStrategy() {
		RouterStrategy rs = strategy.get();
		if (rs != null) {
			try {
				rs.setLimited(TransactionSynchronizationManager
					.isSynchronizationActive()
				);
			} catch (Exception e) {
				rs.setLimited(false);
				if (log.isErrorEnabled()) {
					log.error(e.getMessage(), e);
				}
			}
			
		}
		return rs;
	}
	
	public static String getRouterSelector(final Object obj) {
		RouterSelector slr = selector.get(obj);
		if (slr == null) {
			return null;
		} else {
//			if (slr.isSyncTs()) {
//				throw new RDBException(
//					"Class unallowed transaction annotation"
//				);
//			}
			return slr.getValue();
		}
	}
	
	@SuppressWarnings("unchecked")
	public static boolean clearRouter() {
		try {
			Object res = resource.get();
			if (res != null) {
				Class clz = res.getClass();
				Method close = clz.getMethod("close");
				if (null != close) {
					close.invoke(res);
				}
			}
		} catch (Exception e) {
			if (log.isErrorEnabled()) {
				log.error(e.getMessage(), e);
			}
		} finally {
			resource.remove();
			final RouterStrategy tail = strategy.get();
			if (tail != null) {
				strategy.remove();
				RouterStrategy head = tail.getHead();
				if (head != null) {
					head.setTail(null);
					strategy.set(head);
					return false;
				}
			}
		}
		return true;
	}
	
	public static class RouterSelector implements Serializable{
		
		private static final long serialVersionUID = 1010683458703041934L;
		
		private Object proxyObj;
		
		private boolean syncDs, syncTs;
		
		private Category category;
		
		private String value;
		
		public static enum Category{
			METHOD, CLASS
		}
		
		/**
		 * @return the proxyObj
		 */
		public Object getProxyObj() {
			return proxyObj;
		}

		/**
		 * @param proxyObj the proxyObj to set
		 */
		public void setProxyObj(Object proxyObj) {
			this.proxyObj = proxyObj;
		}

		/**
		 * @return the syncTs
		 */
		public boolean isSyncTs() {
			return syncTs;
		}

		/**
		 * @param syncTs the syncTs to set
		 */
		public void setSyncTs(boolean syncTs) {
			this.syncTs = syncTs;
		}

		/**
		 * @return the syncDs
		 */
		public boolean isSyncDs() {
			return syncDs;
		}

		/**
		 * @param syncDs the syncDs to set
		 */
		public void setSyncDs(boolean syncDs) {
			this.syncDs = syncDs;
		}

		/**
		 * @return the category
		 */
		public Category getCategory() {
			return category;
		}

		/**
		 * @param category the category to set
		 */
		public void setCategory(Category category) {
			this.category = category;
		}

		/**
		 * @return the value
		 */
		public String getValue() {
			return value;
		}

		/**
		 * @param value the value to set
		 */
		public void setValue(String value) {
			this.value = value;
		}
	}
	
	public static class RouterStrategy implements Serializable{
		
		private static final long serialVersionUID = 976720113937831880L;
		
		private RouterStrategy head = null, tail = null;
		
		/** 策略键值 */
		private String key;
		
		/** 策略更新 */
		private boolean refresh;

		/** 限制状态 */
		private boolean limited;
		
		/**
		 * @return the head
		 */
		public RouterStrategy getHead() {
			return head;
		}

		/**
		 * @param head the head to set
		 */
		public void setHead(RouterStrategy head) {
			this.head = head;
		}

		/**
		 * @return the tail
		 */
		public RouterStrategy getTail() {
			return tail;
		}

		/**
		 * @param tail the tail to set
		 */
		public void setTail(RouterStrategy tail) {
			this.tail = tail;
		}
		

		/**
		 * @return the key
		 */
		public String getKey() {
			return key;
		}

		/**
		 * @param key the key to set
		 */
		public void setKey(String key) {
			if (key == null) {
				throw new NullPointerException("key");
			} else 
			if (this.key == null) {
				this.key = key;
				this.refresh = true;
			} else 
			if (!this.key.equals(key)) {
				this.key = key;
				this.refresh = true;
			}
		}

		/**
		 * @return the refresh
		 */
		public boolean isRefresh() {
			return refresh;
		}

		/**
		 * @param refresh the refresh to set
		 */
		public void setRefresh(boolean refresh) {
			this.refresh = refresh;
		}

		/**
		 * @param limited the limited to set
		 */
		public void setLimited(boolean limited) {
			this.limited = limited;
		}

		/**
		 * @return the limited
		 */
		public boolean isLimited() {
			return limited;
		}

		@Override
		public String toString() {
			return "[key=" + key + "][refresh=" + refresh + "][limited=" + limited + "]";
		}
	}
}